package com.zjl.spring.第04章_AOP;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;

@Configuration//(proxyBeanMethods = false)//这是一个配置类 == 配置文件告诉spring boot这是一个配置类 主要
@ComponentScan("com.zjl.spring.第04章_AOP")
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启AspectJ 生成代理对象
public class E_AOP操作_基于AspectJ注解 {

    //static ApplicationContext context = new ClassPathXmlApplicationContext("AOP.xml");//主要用到AOP.xml的扫描

    public static void main(String[] args) {
        //没用xml文件  和xml增强方法的执行顺序不一样
        ApplicationContext context =
                new AnnotationConfigApplicationContext(E_AOP操作_基于AspectJ注解.class);
        /**
         * 1.被增强类
         * 2.增强类(增强的代码)
         * 3.进行通知配置
         *      1.开启注解扫描  (需要xml增加名称空间 context 注解扫描 扫描路径)
         *      2.使用注解创建关于增强的方法 (需要xml增加名称空间 aop )
         *      3.加上@Component注解  增强类额外添加
         * 4、配置不同类型的通知
         *   （1）在增强类的里面，在作为通知方法上面添加通知类型注解，使用切入点表达式配置
         * 5. 相同切入点抽取
         * 6.有多个增强类多同一个方法进行增强，设置增强类优先级
         *      @Order(1) //如果有多个增强类  设置优先级  越小优先级越高
         *
         */
        AspectJ_主 aspectJ_主 = context.getBean("aspectJ_主", AspectJ_主.class);
        System.out.println(aspectJ_主.getA());
        System.out.println("************************执行别的方法********************");
        AspectJ_主2 aspectJ_主2 = context.getBean("aspectJ_主2", AspectJ_主2.class);
        System.out.println(aspectJ_主2.getA(5));
        System.out.println("************************获取对象的class********************");
        //查看class  发现这个类是被改变过得，不是单纯的 AspectJ_主
        //class com.zjl.spring.第04章_AOP.AspectJ_主$$SpringCGLIB$$0
        System.out.println(aspectJ_主.getClass());

        Class<?> superclass = aspectJ_主.getClass().getSuperclass();//获取父类
        //表明是 对原来的类进行了再写
        System.out.println(superclass);//class com.zjl.spring.第04章_AOP.AspectJ_主

        System.out.println("****************接口继承了被代理的接口******");

        Class<?>[] interfaces = aspectJ_主.getClass().getInterfaces();//获取实现的接口
        for (Class<?> in:interfaces){
            //interface org.springframework.aop.SpringProxy
            //interface org.springframework.aop.framework.Advised
            //interface org.springframework.cglib.proxy.Factory
            System.out.println(in);
        }
        System.out.println("----------------只能是spring注入的才能代理，自己new的不可以--------------");
        AspectJ_主 a = new AspectJ_主();
        a.getA();
    }
}

@Component //默认名字是第一个字母变小写
class AspectJ_主{

    @Deprecated(since = "fq")//表示已废弃
    public String getA(){
//        int i=1/0;
        System.out.println("主方法");
        return "0";//没有环绕通知  返回此值
    }
}
@Component //默认名字是第一个字母变小写
class AspectJ_主2{
    public String getA(int a){
//        int i=1/0;
        System.out.println("主方法" + a);
        return "初始返回";//没有环绕通知  返回此值
    }
}


@Component
@Aspect //在xml中配置   只要找到这个类 会自动生成代理对象
//@Order(1)//如果有多个增强类  设置优先级  越小优先级越高  先执行，优先级只适用于同类型的方式，如不同的切面方式，优先级另算
class AspectJ_副{//执行的顺序，不同版本可能不同

    //相同切入点抽取
    @Pointcut("execution(public String com.zjl.spring.第04章_AOP.AspectJ_主.getA())")//以方法作为切入点
//    @Pointcut("@annotation(java.lang.Deprecated)")//以注解作为切入点  即有次注解的会被增强
    public void pointDemo(){

    }

    @Order(1)//这种也行  但是优先级 比上一种底  即使他显示的优先级很高
    @Before(value = "execution(public String *AspectJ_主2.getA(..)) && args(a)",argNames = "a")
    public String Before3(JoinPoint joinPoint,int a){//JoinPoint 获取调用的增强类信息
        System.out.println("前置通知----");
        //获取执行此通知的 类  不要执行，否则就是递归
        System.out.println(joinPoint.getThis().getClass());
        Signature signature = joinPoint.getSignature();//返回增强方法的信息
        System.out.println(signature.getName());//方法吗名
        System.out.println(signature.getDeclaringType());//增强的方法的类型
        System.out.println("获取被增强的方法的入参的值：" + a);
        Object[] args = joinPoint.getArgs();//此方法也可以获取 参数的值
        System.out.println(args[0]);
        return "前置通知";
    }

    //前置通知
    //@Before 注解表示作为前置通知
    @Order(1)
    @Before("pointDemo()")
    public String Before(JoinPoint joinPoint){//只有环绕通知可以在他前面
        System.out.println("增强的方法之_______前置通知 优先级1");
        return "前置通知1";
    }

    @Order(2)
    @Before("pointDemo()")
    public String Before1(){//只有环绕通知可以在他前面
        System.out.println("增强的方法之_______前置通知 优先级2");
        return "前置通知2";
    }

    //可以得到被增强类之前返回的返回值，returning的值 要和方法的变量名一致   如果类型不能赋值，则相当于不执行此增强类
    @AfterReturning(value = "pointDemo()",returning = "re")
    public String AfterReturning(String re){//主方法一执行完就执行
        System.out.println("增强的方法之_______返回通知--" + re);
        return "返回通知";
    }

    @After("pointDemo()")
    public String After(){ //finally  必定执行 而且一定在方法执行完  只有环绕通知可以在他后面
        System.out.println("增强的方法之_______最终通知");
        return "最终通知";
    }

    @AfterThrowing(value = "pointDemo()",throwing = "ex")//可以获取 捕捉的异常信息  名字必须和方法的入参变量名一致
    public String AfterThrowing(Throwable ex){ //发生异常时执行
        System.out.println("增强的方法之_______异常通知");
        return "异常通知";
    }
    @Order(2)
    @Around("pointDemo()") // 所以说此方法必定   在第一行 或者  最后一行(或者异常不执行)
    public String Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("增强的方法之_______环绕通知前");
        proceedingJoinPoint.proceed();//主方法(包括给他增强的方法)  不执行，就真的不执行
//        proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());//可以 添加新的入参
        System.out.println("增强的方法之_______环绕通知后");
        return "环绕通知";//只有环绕通知能直接获得返回值
    }

    @Order(1)//即使优先级高，凡是  注解形式的比切入点的优先级整体底
    @Around("@annotation(java.lang.Deprecated)") //面向注解的增强   由此注解的才会增强
    public String Aroundzj(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("注解增强的方法前");
        proceedingJoinPoint.proceed();
        System.out.println("注解增强的方法后");
        return "注解增强的方法";
    }
}
